/** @file        docksever.h
  * @brief       用来监听ESP8266的请求，作为服务器，
  *              现阶段帮助ESP8266传送的人脸进行识别，
  *              判断数据库中是否存在此人
  * @details   
  * @author      皓瑞
  * @date        2021-03-16
  * @version     V1.0
  * @copyright   
  **********************************************************************************
  * @attention
  **********************************************************************************
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include "baiduyun/docksever.h"
#include <iostream>

using namespace std;
using namespace Json;
using namespace BaiDuYun;
using namespace DockSever;

namespace DockSever{

/**
   * @brief   检测图像中是否存在家庭的成员
   * @param   imageRGB565Data 图像的RGB565原始数据
   * @param   width:   图像宽度
   * @param   height:  图像高度 
   * @return  none
   * @note    
   */
static string isFamily(const char* imageRGB565Data, long width, long height)
{
  /* 将原始的RGB565数据转化为BMP的标准形式 */
  string bmpData = Rgb565Bmp::rgb565Bmp(imageRGB565Data, width, height);

  const string ak = API_KEY; 
  const string sk = SECRET_KEY;
  /* 建立 face句柄，并向服务器请求密钥 */ 
  Face face(ak, sk);
  /* 配置设置 */
  face["image_type"] = "BASE64";
  face["liveness_control"]  = "LOW";

  if(face.FaceSearch(bmpData)){
    return string("Success");
  }else{
    return face.curErrorMsg;
  }
}

/**
   * @brief   客户端处理函数
   * @param   arg： 实际类型为套接字
   * @return  none
   * @note    
   */
static void* cliDataHandle(void * arg)
{
  int tcp_acceptSocket = * reinterpret_cast<int *>(arg);
  /* 和客户端进行读写操作 */
  int ret = -1;
  /* 预分配缓冲区 */
  char* buf = (char *)malloc(sizeof(char)*BUFSIZE);
  
  /* 告诉客户端连接建立成功 */
  write(tcp_acceptSocket, "Connect Succesfully", sizeof("Connect Succesfully"));

  while(1){
    /* 缓冲区清空 */
    memset(buf, 0, BUFSIZE);

    /* 接受数据首部 */
    do{
      ret = read(tcp_acceptSocket, buf, BUFSIZE-1);
    }while(ret<0 && EINTR == errno);
    printf("数据长度:需求%d 实际%d\n", (int)sizeof(DockMsg), (int)ret);
    if(!ret){
      printf("客户端通信错误(1)");
      exit(-1);
    }else{
      if(ret != sizeof(DockMsg)){
        printf("客户端通信错误(1)\n");
        printf("数据长度:需求%d 实际%d\n", (int)sizeof(DockMsg), (int)ret);
        exit(-1);
      }else{
        
        /* 保存图像信息 */
        DockMsg dockMsg;
        memcpy(&dockMsg, buf, sizeof(DockMsg));
        printf("数据宽度:%d 图像高度:%d\n", dockMsg.width, dockMsg.height);
        /* 分配缓冲区 */
        char * imageBuf = (char *)malloc(sizeof(char)*dockMsg.width*dockMsg.height*2);
        /* 缓冲区清空 */
        memset(imageBuf, 0, sizeof(char)*dockMsg.width*dockMsg.height*2);
        int curRcvSize = 0;             // 当前接受的图像数据长度
        /* 循环直至读完整张图像 */
        while(1){
          /* 开始接受图像数据 */
          do{
              ret = read(tcp_acceptSocket, imageBuf+curRcvSize, sizeof(char)*dockMsg.width*dockMsg.height*2-curRcvSize); 
          }while(ret<0 && EINTR == errno);
          if(ret < 0){
            perror("read:");
            exit(-1);
          }
          /* 如果还没有读完全部数据，就继续读取数据 */
          if(curRcvSize+ret < dockMsg.width*dockMsg.height*2){
            curRcvSize += ret;
            printf("curRcvSize=%d\n", curRcvSize);
          }else if(curRcvSize+ret == dockMsg.width*dockMsg.height*2){
            curRcvSize += ret;
            printf("curRcvSize=%d\n", curRcvSize);
            /* 准备处理函数 */
            /* 0000000054030000e0010000 */
            string result;
            /* 获取处理结果 */
            result = isFamily(imageBuf, dockMsg.width, dockMsg.height);
            /* 向服务器发送处理结果 */
            write(tcp_acceptSocket ,result.c_str(), result.size());
            break;
          }else{
            printf("图像大小:%d\n", dockMsg.width*dockMsg.height*2);
            printf("客户端通信错误(2)");
          }
        }

      }
    }

  }
  
  /* 关闭套接字 */
  close(tcp_acceptSocket);

  /* 释放缓冲区 */
  free(buf);
  
  return NULL;
}

void DockSeverInit()
{
    /*! 创建套接字 */
    int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(serv_sock == -1){
      perror("socket:");
      exit(-1);
    }

    /*! 创建sockaddr_in结构体变量 */
    struct sockaddr_in serv_addr;
    /*! 初始化sockaddr_in */
    memset(&serv_addr, 0, sizeof(serv_addr));  
    serv_addr.sin_family = AF_INET;                      //使用IPv4地址
    serv_addr.sin_addr.s_addr = inet_addr(DOCKSEVERIP);  //服务器IP地址
    serv_addr.sin_port = htons(DOCKSEVERPORT);           //服务器端口
    /*! 将套接字和IP、端口绑定 */
    if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1){
      perror("bind:");
      exit(-1);
    }
    

    /*! 进入监听状态，等待用户发起请求 */
    if(listen(serv_sock, 20) == -1){
      perror("listen:");
      exit(-1);
    }
    
    /* 建立线程ID */
    pthread_t tid;

    /* 建立客户端的sockaddr_in结构体 */
    struct sockaddr_in cin;
    socklen_t addrlen = sizeof(cin);
    int tcp_acceptSocket = -1;

    while(1){
        /* 等待客户端连接 */
        tcp_acceptSocket = accept(serv_sock, (struct sockaddr*)&cin, &addrlen);
        if(tcp_acceptSocket<0){
          perror("accept");
          exit(-1);
        }
        char ipv4_addr[16];
        if( !inet_ntop(AF_INET, (void *)&cin.sin_addr, ipv4_addr, sizeof(cin))){
          perror("inet_ntop");
          exit(-1);
        }
        
        printf("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(cin.sin_port));

        /* 创建新的线程处理客户端请求 */
        pthread_create(&tid, NULL, cliDataHandle, (void *)&tcp_acceptSocket);
    }
    
    /* 关闭套接字 */
    close(serv_sock);
}

}